<?php

namespace Swof\Routing;

use Closure;
use Exception;
use HttpResponseException;
use ReflectionException;
use Swof\Container\Container;

class Route
{
    use RouteDependencyResolverTrait;

    /**
     * The URI pattern the route responds to.
     *
     * @var string
     */
    public $uri;

    /**
     * The HTTP methods the route responds to.
     *
     * @var array
     */
    public $methods;

    /**
     * The route action array.
     *
     * @var Closure|string
     */
    public $action;

    /**
     * The controller instance.
     *
     * @var mixed
     */
    public $controller;

    /**
     * 根目录命名空间
     *
     * @var string
     */
    protected $namespace = 'App\\Http\\Controllers';

    /**
     * The container instance used by the route.
     *
     * @var Container
     */
    protected $container;

    public function __construct($methods, $uri, $action)
    {
        $this->uri = $uri;
        $this->methods = (array) $methods;
        $this->action = $action;
    }

    /**
     * Get the domain defined for the route.
     *
     * @return string|null
     */
    public function getDomain(): ?string
    {
        return null;
    }

    /**
     * Get the URI associated with the route.
     *
     * @return string
     */
    public function uri(): string
    {
        return $this->uri;
    }

    /**
     * Get the HTTP verbs the route responds to.
     *
     * @return array
     */
    public function methods(): array
    {
        return $this->methods;
    }

    /**
     * Run the route action and return the response.
     *
     * @return mixed
     * @throws Exception
     */
    public function run(array $array = [])
    {
        $this->container = $this->container ?: new Container();

        try {
            if ($this->isControllerAction()) {
                return $this->runController($array);
            }

            return $this->runCallable();
        } catch (HttpResponseException $e) {
            return $e->getResponse();
        }
    }

    /**
     * Checks whether the route's action is a controller.
     *
     * @return bool
     */
    protected function isControllerAction(): bool
    {
        return is_string($this->action);
    }

    /**
     * Note: 运行类
     *
     * @return mixed
     * @throws Exception
     * @author: Zhao Zhiqiang
     * @since: 2021/5/26 14:15
     */
    protected function runController(array $array = [])
    {
        return $this->dispatch($this->getController(), $this->getControllerMethod(), $array);
    }

    /**
     * Get the controller instance for the route.
     *
     * @return mixed
     * @throws Exception
     */
    public function getController()
    {
        if (! $this->controller) {
            $class = $this->parseControllerCallback()[0];

            $class = strpos($class, '\\') !== 0
                ? $this->namespace . '\\' . $class
                : $class;

            $this->controller = $this->container->make(ltrim($class, '\\'));
        }

        return $this->controller;
    }

    /**
     * Get the controller method used for the route.
     *
     * @return string
     */
    protected function getControllerMethod(): string
    {
        return $this->parseControllerCallback()[1];
    }

    /**
     * Note: Dispatch
     *
     * @param Object $controller
     * @param string $method
     * @return mixed
     * @author: Zhao Zhiqiang
     * @since: 2021/5/30 20:34
     */
    public function dispatch(Object $controller, string $method, array $array = [])
    {
        // $parameters = $this->resolveClassMethodDependencies([], $controller, $method);

        return $controller->{$method}(...array_values($array));
    }

    /**
     * Note: 解析 Controller
     *
     * @return false|string[]
     * @author: Zhao Zhiqiang
     * @since: 2021/5/26 11:54
     */
    protected function parseControllerCallback()
    {
        return explode('@', $this->action, 2);
    }

    /**
     * Note: 执行闭包
     *
     * @return mixed
     * @author: Zhao Zhiqiang
     * @since: 2021/5/26 14:56
     */
    protected function runCallable()
    {
        $closure = $this->action;

        return $closure();
    }
}